/* synth.c implements the core code for audio generation in the asciiboard
   engine.  The synthesiser uses a simple form of wave table synthesis.
   There are two sets of wave tables, envelopes, and mapping of tone
   characters to frequencies.  This means there are two independent audio
   channels which can be mixed together, or modulate each other (AM or FM).

   The wave table is just an array of floating point samples.  To generate
   a given frequency the table is read at a specific rate.  Linear
   interpolation is used to calculate fractional table values.

   The synth maintains its state until a character is sent to it.  Certain
   characters are recognised by the synth and cause it to change (see
   asciiboard_const.h).  When a next period code (space) is received, the
   synthesiser will generate a period of audio, output to a user defined
   buffer.

   The synth functions shouldn't need be referenced outside of the engine
   code.

   Copyright (C) 2014 James Boyle

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
#include <stddef.h>
#include <stdlib.h>
#include "synth.h"
#include "asciiboard.h"
#include "asciiboard_const.h"

#ifdef DEBUG
#include <stdio.h>
#endif

/* static function forward declarations */
/* oscillator */
static void osc_init(OSCILLATOR *const curr_osc);
static void table_osc_init(TABLE_OSC *const curr_osc, float *const table,
                           float *const env, const int table_len,
                           const int env_len);

/* tone management */
static void tone_init(TONE_MANAGER *const tm);
static int get_free_osc(TONE_MANAGER *const tm);
static void return_osc(TONE_MANAGER *const tm, const int tone_num);
static void add_sounding(TONE_MANAGER *const tm, const int tone_code);
static int remove_sounding(TONE_MANAGER *const tm, const int tone_code);
static int tone_swap(TONE_MANAGER *const tm, const int swap);
static int tone_update(TONE_MANAGER *const tm, const char ch);

/* audio generation */
static void update_guardpoint(SYNTH *const s, const int chan);
static float interpolate_wave(const float *wave, const int base_index,
                              const float curr_phase);
static float table_tick(TABLE_OSC *const table_osc, const float freq);
static float synth_tick(SYNTH *const s);
static int next_period(SYNTH *const s);

/* envelope */
static float calculate_env_incr(const float *env, const int index);
static void sync_envelope(SYNTH *const s, const int chan);
static void sync_env_pos(TABLE_OSC *const dest, const TABLE_OSC *const copy);
static void update_envelope_len(SYNTH *const s, const int len, const int chan);
static float envelope_tick(TABLE_OSC *const osc);

/* data streaming */
static void write_val(SYNTH *const s);
static void wave_rw_set(SYNTH *const s, const int chan);
static void envelope_rw_set(SYNTH *const s, const int chan);
static void map_rw_set(SYNTH *const s, const int chan);
static int stream_data(SYNTH *const, const char);
static void reset_env_pos(TABLE_OSC *const osc);

/*  --------------------------------------------------------------------------*/
/* initialising functions */

/* osc_init() initialises the oscillator struct to a set of initial values, by
   default the frequency is 0 ie the oscillator is off. */
void osc_init(OSCILLATOR *const curr_osc)
{
	curr_osc->twopiovrsr = TWOPI / (float) SRATE;
	curr_osc->curr_freq = 0.0f;
	curr_osc->curr_phase = 0.0f;
	curr_osc->incr = 0.0f;
}

/* table_osc_init() initialises the given table oscillator, calculating useful
   constants such as the table size over sample rate.  The table remains a
   fixed size during execution. */
void table_osc_init(TABLE_OSC *const curr_osc, float *const table,
                    float *const env, const int table_len,
                    const int env_len)
{
	curr_osc->table = table;
	curr_osc->table_len = (float) table_len;
	curr_osc->env = env;
	curr_osc->env_len = env_len;
	curr_osc->env_hold = FALSE;

	reset_env_pos(curr_osc);
	curr_osc->sizeovsr = curr_osc->table_len / (float) SRATE;
	osc_init(&curr_osc->osc);
}

/* synth_init() initialises the state of the synthesiser to a default state,
   and sets the audio and save callback functions. */
void synth_init(SYNTH *const s, float *audio_buffer, const int buffer_size,
                void (*af)(const float *, const int, void *), void *af_args)
{
	/* store the users audio outputting function and audio buffer to store
	   generated samples */
	s->audio_func = af;
	s->audio_func_args = af_args;
	/* set the audio buffer */
	s->audio_buffer = audio_buffer;
	s->audio_buffer_size = buffer_size;
	/* reset to the default state */
	synth_reset(s);
}

/* synth_reset() resets the synth to the default state. */
void synth_reset(SYNTH *const s)
{
	int i;

	/* zero the output buffer, not needed in final program */
	for (i = 0; i < s->audio_buffer_size; i++)
		s->audio_buffer[i] = 0.0f;

	/* set the wave tables to a default waveform (sine wave) */
	for (i = 0; i < WAVE_SIZE; i++)
		s->wave_table[CH_ONE][i] = s->wave_table[CH_TWO][i] = default_wave[i];
	/* set the guard point */
	s->wave_table[CH_ONE][i] = s->wave_table[CH_TWO][i] = default_wave[0];

	/* initialise the envelopes */
	s->envelope_len[CH_ONE] = s->envelope_len[CH_TWO] = 1;
	/* set the first envelope to a single 1.0 ie do nothing */
	s->envelope[CH_ONE][0] = 1.0f;
	/* set the second envelope to a single 0.0 i.e. silenced */
	s->envelope[CH_TWO][0] = 0.0f;
	/* set the tone maps to a default set of frequencies and by default all
	   are "off" */
	for (i = 0; i < TONE_COUNT; i++)
		s->tone_map[CH_ONE][i] = s->tone_map[CH_TWO][i] = default_tone_map[i];

	/* initialise all oscillators. Note that each channel has it's
       own oscillators. */
	for (i = 0; i < MAX_POLYPHONY; i++)
	{
		table_osc_init(&(s->tone_data.out[CH_ONE][i]),
		               s->wave_table[CH_ONE],
		               s->envelope[CH_ONE], WAVE_SIZE,
		               s->envelope_len[CH_ONE]);
		table_osc_init(&(s->tone_data.out[CH_TWO][i]),
		               s->wave_table[CH_TWO],
		               s->envelope[CH_TWO], WAVE_SIZE,
		               s->envelope_len[CH_TWO]);
	}
	tone_init(&s->tone_data);

	/* reset any data streaming */
	s->rw = READ;
	s->write_pos = 0;
	s->val_pos = 0;

	/* reset any other flags */
	s->silent = FALSE;
	s->mix_mode = ADD;
}

/*  --------------------------------------------------------------------------*/
/* tone management functions */

/* tone_init() initialises the tone management structure, turning all turns
   off and resetting the linked list of sounding tones.  Flags are reset to
   their default values. */
void tone_init(TONE_MANAGER *const tm)
{
	int i;

	/* set all tones as off and unlinked */
	for (i = 0; i < TONE_COUNT; i++)
	{
		tm->tones[i].tone_code = i + 'a';
		tm->tones[i].state = TONE_OFF;
		tm->tones[i].next_sounding = tm->tones[i].prev_sounding = END_OF_LIST;
	}
	tm->first_sounding = tm->last_sounding = END_OF_LIST;
	tm->sounding_count = 0;

	/* mark all oscillators as available */
	for (i = 0; i < MAX_POLYPHONY; i++)
		tm->available_osc[i] = i;

	/* by default envelopes aren't synced - each tone has it's own position
	   in the envelope */
	tm->envelopes_synced = FALSE;
	tm->silent = FALSE;
}

/* get_free_osc() pops a free oscillator off the stack of free oscillators,
   returning TONE_NO_SOUND if there are no free oscillators.  The count
   of sounding tones is updated. */
int get_free_osc(TONE_MANAGER *const tm)
{
	int available_top = (MAX_POLYPHONY - tm->sounding_count) - 1;
	int osc;

	if (available_top >= 0) /* if oscillator available */
	{
		/* pop oscillator off the stack */
		osc = tm->available_osc[available_top];
		tm->sounding_count++;
		return osc;
	}
	else
		return INVALID_OSC;
}

/* return_osc() pushes a freed oscillator onto the stack of free oscillators.
   The count of sounding tones is updated. */
void return_osc(TONE_MANAGER *const tm, const int osc_num)
{
	int available_top;

	tm->sounding_count--;
	if (tm->sounding_count >= 0)
	{
		available_top = (MAX_POLYPHONY - tm->sounding_count) - 1;
		tm->available_osc[available_top] = osc_num;
	}
}

/* add_sounding() adds a new tone to the end of the list of sounding
   tones. */
void add_sounding(TONE_MANAGER *const tm, const int tone_code)
{
	/* link new node into the list */
	tm->tones[tone_code].next_sounding = END_OF_LIST;
	tm->tones[tone_code].prev_sounding = tm->last_sounding;

	/* check for special case when inserting first node into an empty list */
	if (tm->last_sounding == END_OF_LIST)
		tm->first_sounding = tone_code;
	else
		tm->tones[tm->last_sounding].next_sounding = tone_code;
	tm->last_sounding = tone_code;
}

/* remove_sounding() removes a tone from the list of sounding tones, which
   could be at any position.  The state of the removed ton is returned. */
int remove_sounding(TONE_MANAGER *const tm, const int tone_code)
{
	int tmp;

	/* if note is sounding, ie in the list of sounding tones */
	if (tm->tones[tone_code].state != TONE_OFF &&
	    tm->tones[tone_code].state != TONE_NO_SOUND)
	{
		/* update first and last */
		if (tm->first_sounding == tone_code)
			tm->first_sounding = tm->tones[tone_code].next_sounding;
		if (tm->last_sounding == tone_code)
			tm->last_sounding = tm->tones[tone_code].prev_sounding;

		/* relink the next and previous nodes */
		tmp = tm->tones[tone_code].prev_sounding;
		if (tmp != END_OF_LIST)
			tm->tones[tmp].next_sounding = tm->tones[tone_code].next_sounding;
		tmp = tm->tones[tone_code].next_sounding;
		if (tmp != END_OF_LIST)
			tm->tones[tmp].prev_sounding = tm->tones[tone_code].prev_sounding;
	}
	return tm->tones[tone_code].state;
}

/* tone_swap() takes the tone data and a tone.  It scans for a tone that is
   active (e.g. pressed) but not playing (because there may be a limit to
   how many notes can be playing at once).  If it finds a one it swaps
   oscillators, so now this new tone will start playing.  It returns a value
   > 0 if a swap occurred, 0 otherwise. */
int tone_swap(TONE_MANAGER *const tm, const int swap)
{
	int new_active;

	for (new_active = 0; new_active < TONE_COUNT; new_active++)
	{
		/* if a tone is found that is active (e.g. pressed down on a keyboard
		   but isn't playing, swap it with the tone being released */
		if (tm->tones[new_active].state == TONE_NO_SOUND)
		{
			tm->tones[new_active].state = tm->tones[swap].state;
			add_sounding(tm, new_active);
			return TRUE;
		}
	}
	return FALSE;
}

/* tone_update() recieves input characters and manages which notes are
   pressed / held on the synth.  Polyphony is supported, meaning multiple
   notes can be played simultaneously.  A relatively simple scheme is used
   using a list of all 26 tones, a set of available oscillators and a set of
   currently playing tones

   There are enough oscillators to play <MAX_POLYPHONY> notes.
   A list of available oscillators is maintained, when a new note is pressed,
   if there is a free oscillator this tone is assigned to the oscillator.
   Otherwise, the note is marked as held but not playing.  When a tone is
   released, a search is done to find a tone that isn't sounding to swap the
   oscillator.  If no tone is found, the oscillator is made available again. */
int tone_update(TONE_MANAGER *const tm, const char ch)
{
	int first, osc_num, sync_num;
	int tone_num = convert_char_to_tone(ch);

	/* if silent flag is enabled, ignore any input */
	if (tm->silent)
		return NO_ERROR;

	/* when a silence code is processed, enable the silence flag and cancel
	   any playing tones */
	if (ch == SILENCE_CODE)
	{
		tone_init(tm);
		tm->silent = TRUE;
		return NO_ERROR;
	}

	/* check tone character has been sent */
	if (ch < 'a' || ch > 'z')
		return INVALID_ARG;

	/* if note is being pressed */
	if (tm->tones[tone_num].state == TONE_OFF)
	{
		/* if there is a free oscillator, link the tone to the oscillator */
		if (tm->sounding_count < MAX_POLYPHONY)
		{
			/* the available oscillators are maintained as a simple stack,
			   when an oscillator is needed it is popped off the top, and
			   when an oscillator is returned it is pushed back on */
			osc_num = get_free_osc(tm);
			tm->tones[tone_num].state = osc_num;

			/* add this to the list of active tones */
			add_sounding(tm, tone_num);

			/* initialise the envelope position, if the envelopes are synced
			   all envelopes must follow the first active tone */
			if (tm->envelopes_synced && tm->sounding_count > 1)
			{
				/* get the first sounding tone */
				first = tm->first_sounding;

				/* from this get the first active oscillator */
				sync_num = tm->tones[first].state;
				/* sync the oscillator envelopes to the first active
				   oscillator */
				sync_env_pos(&tm->out[CH_ONE][osc_num],
				             &tm->out[CH_ONE][sync_num]);
				sync_env_pos(&tm->out[CH_TWO][osc_num],
				             &tm->out[CH_TWO][sync_num]);
			}
			else /* if envelopes aren't synced, start the envelope */
			{
				reset_env_pos(&tm->out[CH_ONE][osc_num]);
				reset_env_pos(&tm->out[CH_TWO][osc_num]);
			}
		}
		else /* otherwise mark the note as held but not sounding */
			tm->tones[tone_num].state = TONE_NO_SOUND;
	}

	else /* if the note is being released */
	{
		/* if the tone is currently sounding, change the state of the tone
		   and return the oscillator to the list of free oscillators.
		   If there is another tone that is active, but not playing,
		   try and find another tone to replace it, otherwise, free the
		   oscillator */
		if (tm->tones[tone_num].state != TONE_NO_SOUND)
		{
			remove_sounding(tm, tone_num);
			if (!tone_swap(tm, tone_num))
			{
				/* return the oscillator */
				osc_num = tm->tones[tone_num].state;
				return_osc(tm, osc_num);
			}

		}

		/* change the status of the tone to not playing */
		tm->tones[tone_num].state = TONE_OFF;
	}

	return NO_ERROR;
}

/*  --------------------------------------------------------------------------*/
/* audio generation functions */

/* update_guardpoint copies the first value in the wave table to the last 
   value+1, this may seem redundant, however it saves a single comparison
   in synth_tick.  As this is called once for every sample, for every
   oscillator, it is a worthwhile optimisation.  The term guardpoint is
   taken from CSound. */
void update_guardpoint(SYNTH *const s, const int chan)
{
	s->wave_table[chan][WAVE_SIZE] = s->wave_table[chan][0];
}

/* interpolate_wave() implements either truncation for limited processors, or linear
   interpolation between two points - simply draw a straight line from initial to end,
   where frac is the how far in you want to read e.g. 0.5 for halfway.  Note that this
   is a crude approximation, but works well enough. */
#ifdef LIMITED_PROCESSOR
static float interpolate_wave(const float *wave, const int base_index,
                              const float curr_phase)
{
	/* truncation lookup */
	return wave[base_index];
}
#else
static float interpolate_wave(const float *wave, const int base_index,
                              const float curr_phase)
{
	float frac, grad;

	frac = curr_phase - (float) base_index;
	/* linear interpolation */
	grad = wave[base_index] - wave[base_index+1];
	return (wave[base_index] + (frac * grad));
}
#endif

/* table_tick() uses linear interpolation or truncation to produce the next 
   sample from a table oscillator, updating its state ready for the next 
   sample. */
float table_tick(TABLE_OSC *const table_osc, const float freq)
{
	float val, curr_phase = table_osc->osc.curr_phase;
	int base_index;
	float table_len = table_osc->table_len;

	/* if frequency has changed since last lookup, update the oscillator */
	if (table_osc->osc.curr_freq != freq)
	{
		/* a frequency of 0 resets the oscillator */
		if (freq == 0.0f)
			curr_phase = 0.0f;
		table_osc->osc.curr_freq = freq;
		table_osc->osc.incr = table_osc->sizeovsr * table_osc->osc.curr_freq;
	}

	base_index = (int) curr_phase;
	/* use linear interpolation to guess value of table at any point,
	   note that the guard point (extra redundant table entry) means
	   no check needs to be made on base_index+1 */
	val = interpolate_wave(table_osc->table, base_index, curr_phase);

	/* move to the next position in the table for the given frequency */
	curr_phase += table_osc->osc.incr;

	/* check the phase hasn't extended beyond the table */
	while (curr_phase >= table_len)
		curr_phase -= table_len;
	while (curr_phase < 0.0f)
		curr_phase += table_len;

	table_osc->osc.curr_phase = curr_phase;
	return val;
}

/* synth_tick() produces the next sample from the synth, based on the state
   of the synthesiser.  synth_tick() needs to calculate the frequencies to
   generate on each channel.  The channels are mixed, or AM / FM is
   applied based on the variable mix_mode.
   */
float synth_tick(SYNTH *const s)
{
	int curr_tone, osc_num;
	float f, tmp[CHANS], out = {0.0f};
	TABLE_OSC *osc_one, *osc_two;

	/* check first for no sound */
	if (s->tone_data.sounding_count == 0)
		return 0.0f;

	curr_tone = s->tone_data.first_sounding;
	while (curr_tone != END_OF_LIST)
	{
		/* get current oscillator it's mapped to */
		osc_num = s->tone_data.tones[curr_tone].state;
		osc_one = &s->tone_data.out[CH_ONE][osc_num];
		osc_two = &s->tone_data.out[CH_TWO][osc_num];

		tmp[CH_TWO] = table_tick(osc_two,
				                 s->tone_map[CH_TWO][curr_tone]);
		tmp[CH_TWO] *= envelope_tick(osc_two);

		/* if in FM mode, modulate channel 0's frequency using channel 1
		   using a modulation index of 1 */
		if (s->mix_mode == FM)
		{
			/* calculate the new frequency */
			f = s->tone_map[CH_ONE][curr_tone] * (1.0f + tmp[CH_TWO]);
			tmp[CH_ONE] = table_tick(osc_one, f);
			out += tmp[CH_ONE] * envelope_tick(osc_one);
		}
		/* if in AM mode, modulate channel 0's amplitude using channel 1
		   using a modulation index of 1 */
		else if (s->mix_mode == AM)
		{
			tmp[CH_ONE] = table_tick(osc_one,
			                         s->tone_map[CH_ONE][curr_tone]);
			tmp[CH_ONE] *= envelope_tick(osc_one);
			out += tmp[CH_ONE] * tmp[CH_TWO];
		}
		/* if mixing, simply add the two channels (normalised so divide
		   by 2) */
		else /* if s->mix_mod == ADD */
		{
			tmp[CH_ONE] = table_tick(osc_one,
			                         s->tone_map[CH_ONE][curr_tone]);
			tmp[CH_ONE] *= envelope_tick(osc_one);
			out += (tmp[CH_ONE] + tmp[CH_TWO]) / 2.0f;
		}

		curr_tone = s->tone_data.tones[curr_tone].next_sounding;
	}

	return out / MAX_POLYPHONY;
}

/* next_period() generates a period of audio and sets up the synth for the
   next period. */
int next_period(SYNTH *const s)
{
	int i, curr_tone, osc_num, written = 0;
	TABLE_OSC *osc_one, *osc_two;

	/* unset the silence flag */
	s->tone_data.silent = FALSE;

	/* if the user has not given an audio buffer, don't generate any audio */
	if (s->audio_buffer_size < 0)
		return INVALID_AUDIO_BUFFER_SIZE;
	if (s->audio_buffer == NULL)
	{
		if (s->audio_buffer_size > 0)
			return INVALID_AUDIO_BUFFER;
		else
			return NO_ERROR;
	}

	/* generate a period of samples in blocks of size audio_buffer_size, after
	   each block is generated, process the audio using a supplied function */
	while (written < PERIOD_SAMPLES)
	{
		/* fill the buffer with samples, which may be smaller than a
			period */
		for (i = 0; i < s->audio_buffer_size && written < PERIOD_SAMPLES;
				i++, written++)
		{
			/* write generated sample to the user given buffer */
			s->audio_buffer[i] = synth_tick(s);
		}

		/* call user's callback function to process the block of audio samples
			e.g. write the block to a file or output the block to an audio
			driver */
		if (s->audio_func != NULL)
			s->audio_func(s->audio_buffer, i, s->audio_func_args);
	}

	/* update the envelope for all active oscillators */
	curr_tone = s->tone_data.first_sounding;
	while (curr_tone != END_OF_LIST)
	{
		osc_num = s->tone_data.tones[curr_tone].state;
		osc_one = &s->tone_data.out[CH_ONE][osc_num];
		osc_two = &s->tone_data.out[CH_TWO][osc_num];

		/* if there is another envelope point, update the value and gradient
		   between the next point for efficient linear interpolation; the
		   envelope is read at a rate of a point a period */
		if (osc_one->env_index < osc_one->env_len)
		{
			osc_one->env_index++;
			if (osc_one->env_index < osc_one->env_len-1)
			{
				osc_one->env_incr = calculate_env_incr(osc_one->env,
			                                           osc_one->env_index);
			}
			else /* if reading last point */
				osc_one->env_incr = 0.0f;
		}
		if (osc_two->env_index < osc_two->env_len)
		{
			osc_two->env_index++;
			if (osc_two->env_index < osc_two->env_len-1)
			{
				osc_two->env_incr = calculate_env_incr(osc_two->env,
			                                           osc_two->env_index);
			}
			else /* if reading last point */
				osc_two->env_incr = 0.0f;
		}

		/* go to the next sounding tone */
		curr_tone = s->tone_data.tones[curr_tone].next_sounding;
	}

	/* reset the silence flag */
	s->silent = FALSE;

	return NO_ERROR;
}

/*  --------------------------------------------------------------------------*/
/* envelope functions */

/* calculate_env_incr() calculates how much the envelope value should change for
   each sample, using linear interpolation. */
float calculate_env_incr(const float *env, const int index)
{
	return (env[index+1] - env[index]) / PERIOD_SAMPLES;
}

/* sync_env_pos() syncs the envelopes of two table oscillators. */
void sync_env_pos(TABLE_OSC *const dest, const TABLE_OSC *const copy)
{
	dest->env_index = copy->env_index;
	dest->env_incr = copy->env_incr;
	dest->env_val = copy->env_val;
}

/* reset_env_pos() restarts an envelope, recalculating the increment. */
void reset_env_pos(TABLE_OSC *const osc)
{
	osc->env_index = 0;
	osc->env_val = osc->env[0];
	if (osc->env_len > 1)
		osc->env_incr = calculate_env_incr(osc->env, 0);
}

/* update_envelope_len() updates the envelope length for all table
   oscillators. */
void update_envelope_len(SYNTH *const s, const int len, const int chan)
{
	int i;
	TABLE_OSC *osc;

	s->envelope_len[chan] = len;
	/* update every table oscillator to use this new envelope size, and
	   calculate the new gradient, note the position is reset */
	for (i = 0; i < MAX_POLYPHONY; i++)
	{
		osc = &s->tone_data.out[chan][i];
		osc->env_index = 0;
		osc->env_len = s->envelope_len[chan];
		osc->env_val = osc->env[0];
		osc->env_incr = calculate_env_incr(osc->env, osc->env_index);
	}
}

/* envelope_tick() returns the current envelope value.  If you have gone past
   the end of the array, the final envelope value is returned. */
float envelope_tick(TABLE_OSC *const osc)
{
	const int last = osc->env_len-1;
	float val;

	/* return the value of the envelope */
	val = osc->env_val;
	/* if the envelope position is not before the start or beyond the end of
	   the array, calculate the value of the envelope at the next position
	   using linear interpolation */
	if(osc->env_index < last)
		osc->env_val += osc->env_incr;
	else
		val = val;

	return val;
}

/*  --------------------------------------------------------------------------*/
/* data streaming functions */

/* write_val() writes parses the contents of the value buffer and writes the
   value to the write position.  This is required for streaming data to the
   synth using character input. */
void write_val(SYNTH *const s)
{
	float val;

	s->val_buffer[s->val_pos] = '\0';
	val = (float) atof(s->val_buffer);
	switch (s->rw)
	{
	case WRITE_WAVE_ONE:
		s->wave_table[CH_ONE][s->write_pos] = val;
		break;
	case WRITE_WAVE_TWO:
		s->wave_table[CH_TWO][s->write_pos] = val;
		break;
	case WRITE_ENVELOPE_ONE:
		s->envelope[CH_ONE][s->write_pos] = val;
		break;
	case WRITE_ENVELOPE_TWO:
		s->envelope[CH_TWO][s->write_pos] = val;
		break;
	case WRITE_MAP_ONE:
		s->tone_map[CH_ONE][s->write_pos] = val;
		break;
	case WRITE_MAP_TWO:
		s->tone_map[CH_TWO][s->write_pos] = val;
		break;
	}

	/* set up the value buffer for the next number */
	s->val_pos = 0;
}

/* wave_rw_set() sets the state of the synth to writing data to the
   wave table at the given channel. */
void wave_rw_set(SYNTH *const s, const int chan)
{
	if (chan == CH_ONE)
		s->rw = WRITE_WAVE_ONE;
	else
		s->rw = WRITE_WAVE_TWO;
	s->write_pos = 0;
}

/* envelope_rw_set() sets the state of the synth to wrtiting data to the
   envelope of the given channel. */
void envelope_rw_set(SYNTH *const s, const int chan)
{
	if (chan == CH_ONE)
		s->rw = WRITE_ENVELOPE_ONE;
	else
		s->rw = WRITE_ENVELOPE_TWO;
	s->write_pos = 0;
}

/* map_rw_set() sets the state of the synth to writing data to the tone map
   of the given channel. */
void map_rw_set(SYNTH *const s, const int chan)
{
	if (chan == CH_ONE)
		s->rw = WRITE_MAP_ONE;
	else
		s->rw = WRITE_MAP_TWO;
	s->write_pos = 0;
}

/* stream_data() parses user input to allow streaming of floating point data
   from a user to the synthesiser.  Individual characters are sent to the
   synthesiser, producing a series of floating point values which are stored
   in either one of the synthesiser wave tables, envelopes or tone maps.
   Data is transmitted as a series of values (including a decimal point) e.g.
   0.1425423212461. */
int stream_data(SYNTH *const s, const char ch)
{
	int chan;

	/* if ch is a next period code '_', ignore it, this is mainly for realtime
	   applications which will send next period characters at a
	   fast constant rate, so it shouldn't interfere with the
	   stream */
	if (ch == NEXT_CODE)
		return NO_ERROR;

	/* if ch is a number, decimal point or +/-, add it to the buffer */
	if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' || ch == '-')
	{
		/* if the value buffer is full, simply ignore extra characters.
		   continue streaming, but wait for the separator character ',' or
		   the stream to be ended before the buffer is written to value */
		if (s->val_pos >= VAL_BUFFER_SIZE-1)
			return VAL_BUFFER_OVERRUN;
		s->val_buffer[s->val_pos] = ch;
		s->val_pos++;
		return NO_ERROR;
	}

	if (s->rw == RW_WAVE_ONE_CODE || s->rw == RW_ENVELOPE_ONE_CODE ||
		s->rw == RW_MAP_ONE_CODE)
		chan = CH_ONE;
	else
		chan = CH_TWO;

	/* a data stream is started by one of the streaming codes, and is stopped
	   by the same code, where at most one stream may be running at any time */
	if (ch == RW_WAVE_ONE_CODE || ch == RW_WAVE_TWO_CODE ||
	    ch == RW_ENVELOPE_ONE_CODE || ch == RW_ENVELOPE_TWO_CODE ||
		ch == RW_MAP_ONE_CODE || ch == RW_MAP_TWO_CODE)
	{
		/* if ch is a different streaming code, return an error but
           continue streaming */
		if(ch != s->rw)
			return INVALID_DATA_STREAM;
		else /* if ch is the same streaming code */
		{
			/* write the last value and stop the stream */
			write_val(s);
			if (s->rw == WRITE_WAVE_ONE || s->rw == WRITE_WAVE_TWO)
				update_guardpoint(s, chan);
			else if (s->rw == WRITE_ENVELOPE_ONE ||
			         s->rw == WRITE_ENVELOPE_TWO)
				update_envelope_len(s, s->write_pos+1, chan);

			s->rw = READ;
			return NO_ERROR;
		}
	}

	/* if ch is a value separator, process the value and start buffering
	   the next value */
	if (ch == NEXT_VAL_CODE)
	{
		/* process the current value */
		write_val(s);
		/* set up for the next write */
		s->write_pos++;

		/* check to make sure there is space in the wave table / envelope /
		   tone map to store another element.  Note that it is assumed all
		   arrays are of size >= 1.  If space has run out (unexpectedly), stop
		   streaming and return an error. */
		if ((s->rw == WRITE_WAVE_ONE || s->rw == WRITE_WAVE_TWO)
		    && s->write_pos >= WAVE_SIZE)
		{
			/* set the guardpoint in the wave table */
			update_guardpoint(s, chan);
			/* stop streaming */
			s->rw = READ;
			return DATA_STREAM_OVERRUN;
		}
		else if ((s->rw == WRITE_ENVELOPE_ONE || s->rw == WRITE_ENVELOPE_TWO)
		         && s->write_pos >= ENVELOPE_MAX_SIZE)
		{
			update_envelope_len(s, ENVELOPE_MAX_SIZE, chan);
			/* stop streaming */
			s->rw = READ;
			return DATA_STREAM_OVERRUN;
		}
		else if ((s->rw == WRITE_MAP_ONE || s->rw == WRITE_MAP_TWO)
		         && s->write_pos >= TONE_COUNT)
		{
			/* stop streaming */
			s->rw = READ;
			return DATA_STREAM_OVERRUN;
		}
		else
			return NO_ERROR;
	}

	/* if ch is not a numerical value, decimal point, or a value separator,
	   return an error but continue streaming */
	return INVALID_DATA_STREAM;
}

/* synth functions */
/* wave_copy() allows you to externally change the wave tables.  It takes
   the new wave table and the channel to write.  */
void synth_wave_copy(SYNTH *const s, const float *new_wave,
                     const int chan)
{
	int i;
	float *dest = s->wave_table[chan];

	/* copy the entire contents of the new wave table the synth wave table */
	for (i = 0; i < WAVE_SIZE; i++)
		*dest++ = *new_wave++;
	update_guardpoint(s, chan);
}

/* envelope_copy() allows you to externally change the envelopes.  It takes
   the new envelope array, and its size.  If the envelope is larger than
   the maximum size, only the first ENVELOPE_MAX_SIZE values are copied
   and an error is returned. */
int synth_envelope_copy(SYNTH *const s, const int size, const float *new_env,
                        const int chan)
{
	int i;
	float *dest = s->envelope[chan];

	/* copy the array into the envelope, up to the maximum size of the
	   envelope. */
	for (i = 0; i < size && i < ENVELOPE_MAX_SIZE; i++)
		*dest++ = *new_env++;
	update_envelope_len(s, i, chan);

	/* reset all the envelope positions for playing tones to the start */
	sync_envelope(s, chan);

	if (size > ENVELOPE_MAX_SIZE)
		return COPY_OVERRUN;
	else
		return NO_ERROR;
}

/* synth_map_copy() allows you to externally change the tone maps. */
void synth_map_copy(SYNTH *const s, const float *new_map, const int chan)
{
	int i;
	float *dest = s->tone_map[chan];

	/* copy the the new map over */
	for (i = 0; i < TONE_COUNT; i++)
		*dest++ = *new_map++;
}

/* sync_envelope() synchronises the envelope positions for all sounding
   tones to the first sounding tone */
void sync_envelope(SYNTH *const s, const int chan)
{
	int curr_tone, osc_num;
	TABLE_OSC *osc_dest, *osc_sync;

	/* if a tone is sounding, sync all envelopes to the first sounding one */
	if (s->tone_data.sounding_count > 0)
	{
		curr_tone = s->tone_data.first_sounding;
		osc_num = s->tone_data.tones[curr_tone].state;
		osc_sync = &s->tone_data.out[chan][osc_num];
		curr_tone = s->tone_data.tones[curr_tone].next_sounding;
		while (curr_tone != END_OF_LIST)
		{
			osc_num = s->tone_data.tones[curr_tone].state;
			osc_dest = &s->tone_data.out[chan][osc_num];

			/* sync all the variables */
			sync_env_pos(osc_dest, osc_sync);
			curr_tone = s->tone_data.tones[curr_tone].next_sounding;
		}
	}
}

/* synth_process_ch() processes the current character, updating the state
   of the synth according to the meaning of the code. */
int synth_process_ch(SYNTH *const s, const char ch)
{
	/* check for user reset (DEL code), this takes priority over all other
	   codes, including data streaming */
	if (ch == RESET_CODE)
	{
		synth_reset(s);
		return NO_ERROR;
	}

	/* if data is streaming, process character as data, note that user reset
	   is ignored in this state */
	if (s->rw)
		return stream_data(s, ch);

	/* process any tone controlling characters */
	if ((ch >= 'a' && ch <= 'z') || ch == SILENCE_CODE)
		return tone_update(&s->tone_data, ch);

	/* process additional synth controls */
	switch (ch)
	{
	case NEXT_CODE:
		/* a next period code (space) causes a buffer worth of audio to be
		   produced based on the current state */
		return next_period(s);
	case MIX_CODE:
		s->mix_mode = ADD;
		break;
	case AM_CODE:
		s->mix_mode = AM;
		break;
	case FM_CODE:
		s->mix_mode = FM;
		break;
	case SYNC_ENV_CODE:
		if (s->tone_data.envelopes_synced == FALSE)
		{
			sync_envelope(s, CH_ONE);
			sync_envelope(s, CH_TWO);
			s->tone_data.envelopes_synced = TRUE;
		}
		else /* if envelopes synced */
			s->tone_data.envelopes_synced = FALSE;
		break;
	case RW_WAVE_ONE_CODE:
		wave_rw_set(s, CH_ONE);
		break;
	case RW_WAVE_TWO_CODE:
		wave_rw_set(s, CH_TWO);
		break;
	case RW_ENVELOPE_ONE_CODE:
		envelope_rw_set(s, CH_ONE);
		break;
	case RW_ENVELOPE_TWO_CODE:
		envelope_rw_set(s, CH_TWO);
		break;
	case RW_MAP_ONE_CODE:
		map_rw_set(s, CH_ONE);
		break;
	case RW_MAP_TWO_CODE:
		map_rw_set(s, CH_TWO);
		break;
	/* note that the hold / redundant character (-) does nothing, like most
	   characters it doesn't change the state of the synthesiser.  If a
	   character isn't recognised, it simply passes through and does
	   nothing. */
	case HOLD_CODE:
	default:
		break;
	}

	return NO_ERROR;
}

#ifdef DEBUG
/* synth_tone_debug() is a useful debugging tool - it prints the entire state
   of the tone manager, including all lists (reading both directions) and
   the state of the stack (of free oscillators).  */
void synth_tone_debug(const TONE_MANAGER *const tm)
{
	int i, osc;
	int available_top = (MAX_POLYPHONY - tm->sounding_count) - 1;

	printf("tone[i].state:\n");
	for (i = 0; i < TONE_COUNT; i++)
	{
		printf("Tone %d ('%c'): ",  i, (char) i + 'a');
		if (tm->tones[i].state == TONE_OFF)
			printf("TONE_OFF\n");
		else if (tm->tones[i].state == TONE_NO_SOUND)
			printf("TONE_NO_SOUND\n");
		else
			printf("%d\n", tm->tones[i].state);
	}

	printf("DEBUG: List of sounding tones from first to last:\n");
	for (i = tm->first_sounding; i != END_OF_LIST;
	     i = tm->tones[i].next_sounding)
		 printf("'%c'->", tm->tones[i].tone_code);
	printf("\\END_OF_LIST\n");

	printf("Oscillator frequencies for all active oscillators\n");
	for (i = tm->first_sounding; i != END_OF_LIST;
	     i = tm->tones[i].next_sounding)
	{
		osc = tm->tones[i].state;
		printf("Oscillator %d: %f/%fHz\n", osc,
		       tm->out[CH_ONE][osc].osc.curr_freq,
			   tm->out[CH_TWO][osc].osc.curr_freq);
	}

	printf("DEBUG: List of sounding tones from last to first:\n");
	for (i = tm->last_sounding; i != END_OF_LIST;
	     i = tm->tones[i].prev_sounding)
		 printf("'%c'->", tm->tones[i].tone_code);
	printf("\\END_OF_LIST\n");

	printf("sounding_count = %d\n", tm->sounding_count);
	printf("Current state of free oscillator stack:\n");
	printf("TOP\n");
	for (i = available_top; i >= 0; i--)
		printf("%d\n", tm->available_osc[i]);
	printf("BOTTOM OF STACK.\n");
}
#endif
